
/***************************** 68000 SIMULATOR ****************************

File Name: SIMOPS2.C

This file contains various routines for simulator operation

   Modified: Chuck Kelly
             Monroe County Community College
             http://www.monroeccc.edu/ckelly

***************************************************************************/

#include <stdio.h>
#include <iostream.h>
#include "extern.h"
#include "SIM68Ku.h"
#include "hardwareu.h"

extern int ROMStart, ROMEnd, ReadOnlyStart, ReadOnlyEnd;
extern int ProtectedStart, ProtectedEnd, InvalidStart, InvalidEnd;
extern bool ROMMap, ReadMap, ProtectedMap, InvalidMap;

#define	MODIFY_DATA	0
#define	MODIFY_MEMORY	1

//-----------------------------------------------------------------------
// Close all files
void closeFiles(short *result)
{
  *result = F_SUCCESS;
  for (int i=0; i<MAXFILES; i++) {  // clear file structures
    if (files[i].fp != NULL) {
      if(fclose(files[i].fp) == EOF)
        *result = F_ERROR;
      files[i].fp = NULL;
    }
  }
}

//-----------------------------------------------------------------------
// Open existing file
void openFile(long *fn, char *fname, short *result)
{
  *result = F_ERROR;                    // default to error
  *fn = -1;                             // "
  for (int i=0; i<MAXFILES; i++) {      // find empty file pointer
    if (files[i].fp == NULL) {          // if this file structure is empty
      files[i].fp = fopen(fname, "r+b"); // try to open file for update
      if (files[i].fp != NULL) {        // if success
        strcpy(files[i].name,fname);    // save name of file to file structure
        *result = F_SUCCESS;
        *fn = i;                        // file number is array position
        break;
      }
      files[i].fp = fopen(fname, "rb"); // try to open file for read only
      if (files[i].fp != NULL) {        // if success
        strcpy(files[i].name,fname);    // save name of file to file structure
        *result = F_READONLY;
        *fn = i;                        // file number is array position
        break;
      }
    }
  }
}

//-----------------------------------------------------------------------
// Open new file
void newFile(long *fn, char *fname, short *result)
{
  *result = F_ERROR;                    // default to error
  *fn = -1;                             // "
  for (int i=0; i<MAXFILES; i++) {      // find empty file pointer
    if (files[i].fp == NULL) {          // if this file structure is empty
      files[i].fp = fopen(fname, "w+b"); // open file
      if (files[i].fp != NULL) {        // if success
        strcpy(files[i].name,fname);    // save name of file to file structure
        *result = F_SUCCESS;
        *fn = i;                        // file number is array position
        break;
      }
    }
  }
}

//-----------------------------------------------------------------------
// Read from file
void readFile(long fn, char *buf, unsigned int *size, short *result)
{
  *result = F_ERROR;            // default to error
  if (fn < 0 || fn >= MAXFILES) // if invalid file pointer
    return;
  if (files[fn].fp == NULL)     // if invalid file pointer
    return;

  unsigned int r = fread(buf, 1, *size, files[fn].fp);  // read from file

  if (r < 1) {                  // if error or EOF
    if (feof(files[fn].fp))
      *result = F_EOF;
    else
      *result = F_ERROR;
  } else {
    *result = F_SUCCESS;
    *size = r;                  // number of bytes read
  }

}

//-----------------------------------------------------------------------
// Write to file
void writeFile(long fn, char *buf, unsigned int size, short *result)
{
  *result = F_ERROR;            // default to error
  if (fn < 0 || fn >= MAXFILES) // if invalid file pointer
    return;
  if (files[fn].fp == NULL)     // if invalid file pointer
    return;

  unsigned int r = fwrite(buf, size, 1, files[fn].fp);

  if (r >= 1)                   // if success
    *result = F_SUCCESS;
}

//-----------------------------------------------------------------------
// Position file pointer
void positionFile(long fn, int offset, short *result)
{
  *result = F_ERROR;            // default to error
  if (fn < 0 || fn >= MAXFILES) // if invalid file pointer
    return;
  if (files[fn].fp == NULL)     // if invalid file pointer
    return;

  int r = fseek(files[fn].fp, offset, SEEK_SET);

  if (r == 0)                   // if success
    *result = F_SUCCESS;
}

//-----------------------------------------------------------------------
// Close file
void closeFile(long fn, short *result)
{
  *result = F_ERROR;            // default to error
  if (fn < 0 || fn >= MAXFILES) // if invalid file pointer
    return;
  if (files[fn].fp == NULL)     // if invalid file pointer
    return;

  int r = fclose(files[fn].fp);
  if (r == 0)
    *result = F_SUCCESS;
  files[fn].fp = NULL;          // clear file pointer
}

//-----------------------------------------------------------------------
// Delete file
void deleteFile(char *fname, short *result)
{
  *result = F_ERROR;            // default to error
  if (remove(fname) == 0)
    *result = F_SUCCESS;
}

//-----------------------------------------------------------------------
// Display File Dialog
void displayFileDialog(long *mode, int A1, int A2, int A3, short *result)
{
  char title[256];              // title buffer
  char ext[256];                // extensions
  char path[256];               // file path
  char *inTitle, *inExt, *inPath;
  AnsiString fileName;

  *result = F_SUCCESS;          // default to success
  if (A1 != 0) {
    inTitle = &memory[A1 & ADDRMASK];
    strncpy(title,inTitle,255); // make copy of string so we can terminate it
  } else
    title[0] = '\0';
  title[255] = '\0';        // Terminated!, in case it wasn't already

  if (A2 != 0) {
    inExt = &memory[A2 & ADDRMASK];
    strncpy(ext,inExt,255); // make copy of string so we can terminate it
  } else
    ext[0] = '\0';
  ext[255] = '\0';        // Terminated!, in case it wasn't already

  inPath = &memory[A3 & ADDRMASK];
  strncpy(path,inPath,255); // make copy of string so we can terminate it
  path[255] = '\0';        // Terminated!, in case it wasn't already

  switch (*mode) {
    case 0:             // 'Open' dialog
      if (title[0] != '\0')     // if title present
        Form1->OpenDialogIO->Title = AnsiString(title);
      else
        Form1->OpenDialogIO->Title = "";
      if (ext[0] != '\0')       // if extension list present
        Form1->OpenDialogIO->Filter = AnsiString(ext) + "|" + AnsiString(ext);
      else
        Form1->OpenDialogIO->Filter = "";
      if (path[0] != '\0')      // if file path present
        Form1->OpenDialogIO->FileName = AnsiString(path);
      else
        Form1->OpenDialogIO->FileName = "";
      if (Form1->OpenDialogIO->Execute()) {
        fileName = Form1->OpenDialogIO->FileName;    // get filename
        int nameLength = fileName.Length();
        if (nameLength > 255)                                   // limit length
          nameLength = 255;
        // if buffer overflows 68000 memory
        if ((unsigned int)(A3 + nameLength) > MEMSIZE) {
          *result = F_ERROR;            // error
          return;
        }
        strncpy(inPath,Form1->OpenDialogIO->FileName.c_str(),255);
        inPath[255]='\0';
        *mode = 1;              // d1 = 1 if user opens file
      }else
        *mode = 0;              // d1 = 0 if user cancels
      break;

    case 1:             // 'Save' dialog
      if (title[0] != '\0')     // if title present
        Form1->SaveDialogIO->Title = AnsiString(title);
      else
        Form1->SaveDialogIO->Title = "";
      if (ext[0] != '\0')       // if extension list present
        Form1->SaveDialogIO->Filter = AnsiString(ext) + "|" + AnsiString(ext);
      else
        Form1->SaveDialogIO->Filter = "";
      if (path[0] != '\0')      // if file path present
        Form1->SaveDialogIO->FileName = AnsiString(path);
      else
        Form1->OpenDialogIO->FileName = "";
      if (Form1->SaveDialogIO->Execute()) {
        fileName = Form1->OpenDialogIO->FileName;    // get filename
        int nameLength = fileName.Length();
        if (nameLength > 255)                                   // limit length
          nameLength = 255;
        // if buffer overflows 68000 memory
        if ((unsigned int)(A3 + nameLength) > MEMSIZE) {
          *result = F_ERROR;            // error
          return;
        }
        strncpy(inPath,Form1->SaveDialogIO->FileName.c_str(),255);
        inPath[255]='\0';
        *mode = 1;              // d1 = 1 if user opens file
      }else
        *mode = 0;              // d1 = 0 if user cancels
      break;
  }
}

//-----------------------------------------------------------------------
// Load S Record file
//                               /-version number
//          module name         / /-revision number                     checksum
//S0  0000 6 8 K P R O G       2 0 C R E A T E D   B Y   E A S Y 6 8 K /
//S021000036384B50524F47202020323043524541544544204259204541535936384B6D
//S0  0000             R O M   0 0 0 0 0 0 , 0 0 0 0 0 0cc
//S0  0000           R E A D   0 0 0 0 0 0 , 0 0 0 0 0 0cc
//S0  0000 P R O T E C T E D   0 0 0 0 0 0 , 0 0 0 0 0 0cc
//S0  0000     I N V A L I D   0 0 0 0 0 0 , 0 0 0 0 0 0cc
//S0nn0000
int loadSrec(char *name)        // load memory with contents of s_record file
{
  FILE *fp;
  int bytecount, line, loc, end__of__file;
  char s_byte[4], s_type, nambuf[40];
  char *bufptr, *bufend;
  uchar checksum;
  AnsiString str, str2;
  const AnsiString EASy68K_HEADER = "S0 = 68KPROG   20CREATED BY EASY68K";
  bool EASy68Kv2_0_SRecord = false;
  bool sRecError = false;
  bool skipRecord;              // used to skip loading record data

  try {
    fp = fopen(name, "rt");
    if (fp == NULL) {             // if file cannot be opened, print message
      Form1->Message->Lines->Add(str.sprintf("error: cannot open file %s", name));
      return FAILURE;
    }

    line = 0;
    end__of__file = false;
    s_type = 0;

    while (fgets(lbuf, SREC_MAX, fp) != NULL) { // read file until end
      bufptr = lbuf;
      checksum = 0;
      skipRecord = false;
      line++;
      sscanf (lbuf, "S%c%2x", &s_type, &bytecount);
      bufptr += 4;
      checksum += bytecount;
      switch (s_type) {         // what type of S record ?
        // S0  Description of S-Record
        case '0' :
          skipRecord = true;    // don't load S0 data
          for (bufend = bufptr; *bufend != '\0'; bufend++);  // put bufend at end of line
          if (sscanf(bufptr,"%04x", &loc) != 1) // 2 byte address
            sRecError = true;
          else
            bufptr += 4;
          str = "S0 = ";
          while (sscanf(bufptr,"%2x",&s_byte)) {
            bufptr += 2;
            checksum += s_byte[0];
            if ((bufptr + 2) >= bufend) break;  // if checksum byte
              if (s_byte[0] >= ' ' && s_byte[0] <= '~') // if displayable
                str = str + s_byte[0];          // add character to str
              else
                str = str + '.';                // use '.' for non displayable
          }
          checksum = ~checksum &0xFF;
          if(checksum)                          // if checksum not 0
            Form1->Message->Lines->Add(str2.sprintf("Checksum error on line %d...",line));
          Form1->Message->Lines->Add(str);
          if(EASy68Kv2_0_SRecord)       // if this is an EASy68K 2.0 SRecord file
          {
            // Set Memory Map in Hardware form
            // Does not erase existing map if no map specified
            if(str.SubString(6,9) == "      ROM") {        // if ROM range included
              Hardware->ROMChk->Checked = true;
              Hardware->ROMStartEdit->EditText = str.SubString(16,6);
              Hardware->ROMEndEdit->EditText = str.SubString(23,6);
            } else if(str.SubString(6,9) == "     READ") { // if Read/Only range included
              Hardware->ReadChk->Checked = true;
              Hardware->ReadStartEdit->EditText = str.SubString(16,6);
              Hardware->ReadEndEdit->EditText = str.SubString(23,6);
            } else if(str.SubString(6,9) == "PROTECTED") { // if Protected range included
              Hardware->ProtectedChk->Checked = true;
              Hardware->ProtectedStartEdit->EditText = str.SubString(16,6);
              Hardware->ProtectedEndEdit->EditText = str.SubString(23,6);
            } else if(str.SubString(6,9) == "  INVALID") { // if Invalid range included
              Hardware->InvalidChk->Checked = true;
              Hardware->InvalidStartEdit->EditText = str.SubString(16,6);
              Hardware->InvalidEndEdit->EditText = str.SubString(23,6);
            }
          }else if(str == EASy68K_HEADER)
            EASy68Kv2_0_SRecord = true;
          str = "";
          break;
        // S1 2-byte address
        case '1' :
          if (sscanf(bufptr,"%04x", &loc) != 1)
            sRecError = true;
          else
            bufptr += 4;
          break;
        // S2 3-byte address
        case '2' :
          if (sscanf(bufptr,"%06x", &loc) != 1)
            sRecError = true;
          else
            bufptr += 6;
          break;
        // S3 4-byte address
        case '3' :
          if (sscanf(bufptr,"%08x", &loc) != 1)
            sRecError = true;
          else
            bufptr += 8;
          break;
        // S5 length count
        case '5' :
          skipRecord = true;      // ignore
          break;
        // S7 4-byte starting address
        case '7' :
          if (sscanf(bufptr,"%08x", &loc) != 1)
            sRecError = true;
          else {
            PC = loc;
            end__of__file = true;
          }
          break;
        // S8 3-byte starting address
        case '8' :
          if (sscanf(bufptr,"%06x", &loc) != 1)
            sRecError = true;
          else {
            PC = loc;
            end__of__file = true;
          }
          break;
        // S9 2-byte starting address
        case '9' :
          if (sscanf(bufptr,"%04x", &loc) != 1)
            sRecError = true;
          else {
            PC = loc;
            end__of__file = true;
          }
          break;
        default :  sRecError = true;
      }
      if (end__of__file) break;
      if (sRecError) break;
      if (!skipRecord)
      {
        checksum += loc&0xFF;
        checksum += (loc&0xFF00)>>8;
        checksum += (loc&0xFF0000)>>16;
        checksum += (loc&0xFF000000)>>24;
        for (bufend = bufptr; *bufend != '\0'; bufend++);  // put bufend at end of line
        while (sscanf(bufptr,"%02x",&s_byte)) {
          bufptr += 2;
          checksum += s_byte[0];
          if ((bufptr + 2) >= bufend) break;    // if checksum byte
          if ((loc < 0) || (loc > (MEMSIZE - 1))) {
            Form1->Message->Lines->Add(str.sprintf("Invalid Address on line %d...",line));
            sRecError = true;
            break;
          }
          else
            memory[loc++] = s_byte[0];
        }
        checksum = ~checksum &0xFF;
        if(checksum)                    // if checksum not 0
          Form1->Message->Lines->Add(str.sprintf("Checksum error on line %d...",line));
        if (sRecError) break;
      }
    } // endw read until end of file
    if (sRecError)                     // if error reading file, print message
    {
      Form1->Message->Lines->Add(str.sprintf("Invalid data on line %d of .S68 file...",line));
      Form1->Message->Lines->Add(str.sprintf ("%d: %s", line, lbuf));    // *ck 12-3-2005
      Form1->Message->Lines->Add(str.sprintf("Remainder of load stopped..."));
    }else{
      Form1->Message->Lines->Add(str.sprintf(".S68 file read successful"));
    }
    fclose(fp);			// close file specified
    return SUCCESS;
    }
  catch( ... ) {
    Form1->Message->Lines->Add(str.sprintf("Unexpected Error reading .S68 file"));
    return FAILURE;
  }
}


